﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TookKit.Mvvm.Events
{
    /// <summary>
    /// 事件总线
    /// </summary>
    public sealed class EventBus
    {
        public static EventBus Default { get; } = new EventBus();

        private readonly ConcurrentDictionary<Type, Delegate> _eventMaps;

        public EventBus()
        {
            _eventMaps = new ConcurrentDictionary<Type, Delegate>();
        }

        /// <summary>
        /// 订阅事件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="callback"></param>
        public void Subscribe<T>(Action<object,T> callback) where T : class
        {
            Type messsageType = typeof(T);
            if(_eventMaps.TryGetValue(messsageType,out Delegate @delegate))
            {
                _eventMaps[messsageType] = Delegate.Combine(@delegate, callback);
            }
            else
            {
                _eventMaps.TryAdd(messsageType, callback);
            }
        }

        /// <summary>
        /// 取消订阅
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="callback"></param>
        /// <returns></returns>
        public bool UnSubscribe<T>(Action<object, T> callback) where T : class
        {
            Type messsageType = typeof(T);
            if (_eventMaps.ContainsKey(messsageType))
            {
                return _eventMaps.TryRemove(messsageType, out Delegate @delegate);
            }
            return false;
        }

        /// <summary>
        /// 发布事件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sender"></param>
        /// <param name="message"></param>
        public void Publish<T>(object sender,T message) where T : class
        {
            Type messsageType = typeof(T);

            if(_eventMaps.TryGetValue(messsageType,out Delegate @delegate))
            {
                foreach (var item in @delegate.GetInvocationList())
                {
                    if(item is Action<object, T> callback)
                    {
                        callback?.Invoke(sender, message);
                    }
                }
            }
        }
    }
}
